﻿namespace OpenComposite.Mapper
{
	using System;
	using System.CodeDom;
	using System.CodeDom.Compiler;
	using System.Collections.Generic;
	using System.ComponentModel;
	using System.Data;
	using System.Drawing;
	using System.IO;
	using System.ServiceModel.Description;
	using System.Text;
	using System.Web.Services.Description;
	using System.Windows.Forms;
	using System.Workflow.ComponentModel.Design;

	using Microsoft.CSharp;

	using OpenComposite.EII;
	using OpenComposite.EII.Repository;

	using OpenComposite.Base.Collections;
	using OpenComposite.Repository.Properties;

	public partial class CodeMapperControl : UserControl
	{
		#region Initialization

		public CodeMapperControl()
		{
			InitializeComponent();
			foreach ( DataGridViewColumn col in dgvInput.Columns ) {
				dgvOutput.Columns.Add((DataGridViewColumn)col.Clone());
			}
			init();
		}

		#endregion Initialization

		#region Public Members

		#region Properties

		public string HeaderText
		{
			get { return ""; }
		}

		public string MappingText
		{
			get { return syntaxCodeEditor.Text; }
		}

		public bool UseCodeMapping
		{
			get { return cbxUseCodeMapping.Checked; }
			set
			{
				cbxUseCodeMapping.Checked = value;
				syntaxCodeEditor.Document.ReadOnly = !cbxUseCodeMapping.Checked;
			}
		}

		#endregion Properties

		#endregion Public Members

		#region Internal Members

		#region Methods

		internal void SetMappingText(string value, LogicalActivity logAct, bool isInput)
		{
			SetMappingText(value, isInput);
			_isInput = isInput;
			_logAct = logAct;
			loadLogicalActivity();
		}

		internal void SetMappingText(ISupportLogicalPhysicalMapping lwm, bool isInput)
		{
			string value = isInput ? lwm.InputCodeMapping : lwm.OutputCodeMapping;
			SetMappingText(value, isInput);
			_isInput = isInput;
			_mappingItem = lwm;

			loadLogicalMethod();
		}

		#endregion Methods

		#endregion Internal Members

		#region Private Members

		#region Fields

		BackgroundWorker _bwLoader = null;
		private bool _isInput = false;
		private LogicalActivity _logAct = null;
		private ISupportLogicalPhysicalMapping _mappingItem = null;
		private Keys _rememberedKey = Keys.None;
		private Keys _rememberedNumKey = Keys.None;
		private DataGridView _selectedDataGridView = null;

		#endregion Fields

		#region Event Handlers

		private void _bwLoader_DoWork(object sender, DoWorkEventArgs e)
		{
			string text = _mappingItem.GenerateHeaderText(_isInput);
			e.Result = text;
		}

		private void _bwLoader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			syntaxCodeEditor.HeaderText = e.Result.ToString();
			//syntaxCodeEditor.Document.Reparse();
			progressDisk1.Visible = false;
		}

		private void cbxUseCodeMapping_CheckedChanged(object sender, EventArgs e)
		{
			syntaxCodeEditor.Document.ReadOnly = !cbxUseCodeMapping.Checked;
		}

		private void dgvInput_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
		{
			if ( e.RowIndex < 0 ) return;
			_selectedDataGridView = sender as DataGridView;
			_selectedDataGridView.Rows[e.RowIndex].Selected = true;
		}

		private void insertInitializationToolStripMenuItem_Click(object sender, EventArgs e)
		{
			insertInitialization();
		}

		private void insertReferenceToolStripMenuItem_Click(object sender, EventArgs e)
		{
			insertReference();
		}

		private void syntaxCodeEditor_KeyUp(object sender, KeyEventArgs e)
		{
			if ( e.Alt && e.Control ) {
#if DEBUG
				if ( e.KeyCode == Keys.I ) {
					Form form1 = new Form();
					form1.Size = new Size(900, 700);
					TextBox txtStuff = new TextBox();
					txtStuff.Multiline = true;
					txtStuff.ScrollBars = ScrollBars.Both;
					txtStuff.Font = new Font(FontFamily.GenericMonospace, 10F);
					form1.Controls.Add(txtStuff);
					txtStuff.Dock = DockStyle.Fill;
					txtStuff.Text = syntaxCodeEditor.HeaderText + syntaxCodeEditor.Text + syntaxCodeEditor.FooterText;
					form1.ShowDialog();
				} else if ( e.KeyCode == Keys.D ) {
					//SpanIndicatorLayer layer = syntaxCodeEditor.Document.SpanIndicatorLayers[0];
				} else if ( e.KeyCode == Keys.A ) {
					//syntaxCodeEditor.ShowAboutForm();
				}
#endif
			} else if ( e.Modifiers == Keys.Alt ) {
				if ( _rememberedKey == Keys.None ) {
					if ( e.KeyCode == Keys.I || e.KeyCode == Keys.O ) {
						_rememberedKey = e.KeyCode;
					}
				} else {
					if ( _rememberedNumKey == Keys.None &&
						 e.KeyCode >= Keys.D1 && e.KeyCode <= Keys.D9 ) {
						_rememberedNumKey = e.KeyCode;
					} else {
						int val;
						int.TryParse(( (char)_rememberedNumKey ).ToString(), out val);
						if ( _rememberedKey == Keys.I ) { // use input dgv
							_selectedDataGridView = dgvInput;
						} else if ( _rememberedKey == Keys.O ) { // use output dgv
							_selectedDataGridView = dgvOutput;
						}
						if ( val != 0 && _selectedDataGridView.Rows.Count >= val ) {
							_selectedDataGridView.Rows[val - 1].Selected = true;
						}
						if ( e.KeyCode == Keys.I ) {
							insertInitialization();
						} else if ( e.KeyCode == Keys.R ) {
							insertReference();
						}
						_rememberedKey = Keys.None;
						_rememberedNumKey = Keys.None;
					}
				}
			} else {
				_rememberedKey = Keys.None;
				_rememberedNumKey = Keys.None;
			}
		}

		private void tsbtnInsertInit_Click(object sender, EventArgs e)
		{
			if ( ( (ToolStripItem)sender ).Owner == tsVarMenuInput ) {
				_selectedDataGridView = dgvInput;
			} else {
				_selectedDataGridView = dgvOutput;
			}
			insertInitialization();
		}

		/// <summary>
		/// Handles the Click event of the tsbtnInsertRef control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void tsbtnInsertRef_Click(object sender, EventArgs e)
		{
			if ( ( (ToolStripItem)sender ).Owner == tsVarMenuInput ) {
				_selectedDataGridView = dgvInput;
			} else {
				_selectedDataGridView = dgvOutput;
			}
			insertReference();
		}

		#endregion Event Handlers

		#region Methods

		private void SetHeaderAsync(string value)
		{
			syntaxCodeEditor.HeaderText = value;
		}

		private void SetMappingText(string value, bool isInput)
		{
			if ( string.IsNullOrEmpty(value) ) {
				if ( isInput ) {
					value = @"
/*
use logical input (upper right panel) to fill the physical input variables (lower right panel)
*/";
				} else {
					value = @"
/*
use the logical output variables (upper right panel) to fill the physical output variables (lower right panel)
*/";
				}
			}
			syntaxCodeEditor.Text = value;
		}

		private void init()
		{
			// Just chooses some formatting options, like four space indenting
			CodeGeneratorOptions options = new CodeGeneratorOptions();
			options.BlankLinesBetweenMembers = true;
			options.BracingStyle = "C";
			options.ElseOnClosing = true;
			options.IndentString = "\t";

			syntaxCodeEditor.SetHighlighting("C#");
		}

		private void insertInitialization()
		{
			if ( _selectedDataGridView == null ) return;
			ParameterItem selectedItem = _selectedDataGridView.SelectedRows[0].DataBoundItem as ParameterItem;
			string textToInsert = string.Format("{0} = new {1}();", selectedItem.Name, selectedItem.Type);
			syntaxCodeEditor.Document.Insert(syntaxCodeEditor.ActiveTextAreaControl.Caret.Offset, textToInsert);
		}

		private void insertReference()
		{
			if ( _selectedDataGridView == null ) return;
			ParameterItem selectedItem = _selectedDataGridView.SelectedRows[0].DataBoundItem as ParameterItem;
			string textToInsert = selectedItem.Name;

			syntaxCodeEditor.Document.Insert(syntaxCodeEditor.ActiveTextAreaControl.Caret.Offset, textToInsert);
		}

		private void loadLogicalActivity()
		{
		}

		private void loadLogicalMethod()
		{
			_bwLoader = new BackgroundWorker();

			_bwLoader.DoWork += new DoWorkEventHandler(_bwLoader_DoWork);
			_bwLoader.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_bwLoader_RunWorkerCompleted);
			_bwLoader.RunWorkerAsync();

			BindingListEx<ParameterItem> inputItems = new BindingListEx<ParameterItem>();
			BindingListEx<ParameterItem> outputItems = new BindingListEx<ParameterItem>();
			Bitmap noun = Resources.png_field;
			noun.MakeTransparent();
			Bitmap bmpClass = Resources.class_png;
			bmpClass.MakeTransparent();
			if ( _isInput ) {
				if ( _mappingItem.InputBusinessObject != null ) {
					inputItems.Add(new ParameterItem(Resources.bobject,
						"input0",
						_mappingItem.InputBusinessObject.Name));

					string[] types = null;
					string[] names = null;
					if ( _mappingItem.WebMethod != null ) {
						types = _mappingItem.WebMethod.InputTypeName.Split(';');
						names = _mappingItem.WebMethod.InputVariableName.Split(';');
					} else if ( _mappingItem.CallbackWebMethod != null ) {
						types = _mappingItem.CallbackWebMethod.InputTypeName.Split(';');
						names = _mappingItem.CallbackWebMethod.InputVariableName.Split(';');
					} else if ( _mappingItem.SendMessageQueue != null ) {
						types = new string[] { "object" };
						names = new string[] { "obj" };
					}
					if ( types == null || names == null ) return;
					for ( int i = 0; i < types.Length; i++ ) {
						outputItems.Add(new ParameterItem(noun,
							names.Length < i ? string.Format("output{0}", i) : names[i],
							types[i]));
					}
				} else {
					outputItems.Add(new ParameterItem(noun, "obj", "object"));
				}
			} else if ( _mappingItem.OutputBusinessObject != null ) {
				outputItems.Add(new ParameterItem(Resources.bobject,
					"result",
					_mappingItem.OutputBusinessObject.Name));

				string[] types = null;
				if ( _mappingItem.WebMethod != null ) types = _mappingItem.WebMethod.OutputTypeName.Split(';');
				if ( _mappingItem.CallbackWebMethod != null ) types = _mappingItem.CallbackWebMethod.OutputTypeName.Split(';');
				if ( _mappingItem.ReceiveMessageQueue != null ) types = new string[] { "object" };
				if ( types != null ) {
					for ( int i = 0; i < types.Length; i++ ) {
						inputItems.Add(new ParameterItem(i == 0 ? bmpClass : noun,
							i == 0 ? "input" : string.Format("input{0}", i),
							types[i]));
					}
				}
			}
			dgvInput.DataSource = inputItems;
			dgvOutput.DataSource = outputItems;

			#region Set Header/Footer
			string returnType = "void";
			if ( _mappingItem.OutputBusinessObject != null ) returnType = _mappingItem.OutputBusinessObject.Name;
			lblTopHeader.Text = string.Format("private {0} {1}{2}(...)",
				_isInput ? "bool" : returnType,
				_mappingItem.Name,
				_isInput ? "InputMapping" : "OutputMapping");
			string outputName = null;
			if ( _mappingItem.WebMethod != null ) outputName = _mappingItem.WebMethod.OutputTypeName.Split(';')[0];
			else if ( _mappingItem.CallbackWebMethod != null ) outputName = _mappingItem.CallbackWebMethod.OutputTypeName.Split(';')[0];
			else if ( _mappingItem.SendMessageQueue != null && _mappingItem.ReceiveMessageQueue == null ) outputName = "void";
			if ( !_isInput && _mappingItem.OutputBusinessObject != null ) {
				lblBottomHeader.Text = string.Format("    {0} result = null;",
						_mappingItem.OutputBusinessObject.Name);
			} else if ( _isInput ) {
				lblBottomHeader.Text = "    bool isOk = true;";
			}
			string returnStatement = _isInput ? "return isOk;" : "return result;";
			syntaxCodeEditor.FooterText = string.Format("{0}{2} {1}{0}{1}{0}{1}{0}", Environment.NewLine, "}", returnStatement);
			lblFooter.Text = string.Format("    {2}{0}{1}", Environment.NewLine, "}", returnStatement);
			#endregion
		}

		#endregion Methods

		#endregion Private Members

		#region Nested Types

		private class ParameterItem
		{
			#region Initialization

			public ParameterItem(Image img, string name, string type)
			{
				this.Image = img;
				this.Name = name;
				this.Type = type;
			}

			#endregion Initialization

			#region Public Members

			#region Properties

			public Image Image
			{
				get;
				set;
			}

			public string Name
			{
				get;
				set;
			}

			public string Type
			{
				get;
				set;
			}

			#endregion Properties

			#endregion Public Members
		}

		#endregion Nested Types
	}
}